package com.coderising.jvm.cmd;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.coderising.jvm.clz.ClassFile;
import com.sun.org.apache.bcel.internal.generic.BIPUSH;
import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;

public class CommandParser {

    public static final String aconst_null = "01";
    public static final String new_object = "BB";
    public static final String lstore = "37";
    public static final String invokespecial = "B7";
    public static final String invokevirtual = "B6";
    public static final String getfield = "B4";
    public static final String putfield = "B5";
    public static final String getstatic = "B2";
    public static final String ldc = "12";
    public static final String dup = "59";
    public static final String bipush = "10";
    public static final String aload_0 = "2A";
    public static final String aload_1 = "2B";
    public static final String aload_2 = "2C";
    public static final String iload = "15";
    public static final String iload_1 = "1B";
    public static final String iload_2 = "1C";
    public static final String iload_3 = "1D";
    public static final String fload_3 = "25";

    public static final String voidreturn = "B1";
    public static final String ireturn = "AC";
    public static final String freturn = "AE";

    public static final String astore_1 = "4C";
    public static final String if_icmp_ge = "A2";
    public static final String if_icmple = "A4";
    public static final String goto_no_condition = "A7";
    public static final String iconst_0 = "03";
    public static final String iconst_1 = "04";
    public static final String istore_1 = "3C";
    public static final String istore_2 = "3D";
    public static final String iadd = "60";
    public static final String iinc = "84";

    public static ByteCodeCommand[] parse(ClassFile clzFile, String codes) {

        codes = codes.toUpperCase();
        if (codes == "" || codes == null) {
            throw new RuntimeException("codes为空！");
        }
        List<ByteCodeCommand> cmds = new LinkedList<ByteCodeCommand>();

        CommandIterator iter = new CommandIterator(codes);
        while (iter.hasNext()) {
            String opCode = iter.next2CharAsString();
            if (opCode.equals(new_object)) {
                NewObjectCmd newObjectCmd = new NewObjectCmd(clzFile,opCode);
                newObjectCmd.setOprand1(iter.next2CharAsInt());
                newObjectCmd.setOprand2(iter.next2CharAsInt());
                cmds.add(newObjectCmd);
            } else if (opCode.equals(dup)||opCode.equals(astore_1)||opCode.equals(aload_0)||opCode.equals(aload_1)||opCode.equals(aload_2)||opCode.equals(voidreturn)) {
                NoOperandCmd noOperandCmd = new NoOperandCmd(clzFile, opCode);
                cmds.add(noOperandCmd);

            }else if (opCode.equals(ldc)){
                LdcCmd cmd = new LdcCmd(clzFile, opCode);
                cmd.setOperand(iter.next2CharAsInt());
                cmds.add(cmd);
            }else if (opCode.equals(bipush)){
                BiPushCmd cmd = new BiPushCmd(clzFile, opCode);
                cmd.setOperand(iter.next2CharAsInt());
                cmds.add(cmd);
            } else if (opCode.equals(invokespecial)) {
                InvokeSpecialCmd cmd = new InvokeSpecialCmd(clzFile, opCode);
                cmd.setOprand1(iter.next2CharAsInt());
                cmd.setOprand2(iter.next2CharAsInt());
                cmds.add(cmd);
            } else if (opCode.equals(invokevirtual)) {
                InvokeVirtualCmd cmd = new InvokeVirtualCmd(clzFile, opCode);
                cmd.setOprand1(iter.next2CharAsInt());
                cmd.setOprand2(iter.next2CharAsInt());
                cmds.add(cmd);
            }else{
                throw new RuntimeException(opCode + "未进行判断！");
            }
        }

        calcuateOffset(cmds);
        ByteCodeCommand[] cmd = (ByteCodeCommand[])cmds.toArray();
        return cmd;
    }

    private static void calcuateOffset(List<ByteCodeCommand> cmds) {

        int offset = 0;
        for (ByteCodeCommand cmd : cmds) {
            cmd.setOffset(offset);
            offset += cmd.getLength();
        }

    }

    private static class CommandIterator {
        String codes = null;
        int pos = 0;

        CommandIterator(String codes) {
            this.codes = codes;
        }

        public boolean hasNext() {
            return pos < this.codes.length();
        }

        public String next2CharAsString() {
            String result = codes.substring(pos, pos + 2);
            pos += 2;
            return result;
        }

        public int next2CharAsInt() {
            String s = this.next2CharAsString();
            return Integer.valueOf(s, 16).intValue();
        }

    }
}
